﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;

using SisCRA.Business;
using SisCRA.Classes;
using SisCRA.Config;

namespace SisCRA
{
    public class Mail
    {
        #region -Constants-

        private const string SMTP_HOST = "smtp.gmail.com";
        private const int SMTP_PORT = 587;

        #endregion

        #region -Private Attributes-

        private static Mail instance;

        private SmtpClient smtpClient;

        #endregion

        #region -Constructor-

        /// <summary>
        /// Construtor padrão.
        /// </summary>
        private Mail()
        {
            
        }

        #endregion

        #region -Singleton-

        /// <summary>
        /// Retorna uma instância da classe Mail.
        /// </summary>
        public static Mail Instance
        {
            get 
            {
                if (instance == null)
                {
                    instance = new Mail();
                }
                
                return instance;
            }
        }

        #endregion

        #region -Public Methods-

        /// <summary>
        /// Método que envia ao aluno um e-mail de aviso de notas.
        /// </summary>
        /// <param name="studentEnrollmentAvaliation">Objeto Avaliação</param>
        public void SendGradesMail(StudentEnrollmentAvaliation studentEnrollmentAvaliation)
        {
            try
            {
                MailAddress from = new MailAddress(GetTeacherEmail(), GetTeacherName());
                MailAddress to = new MailAddress(GetStudentEmail(studentEnrollmentAvaliation.StudentId), GetStudentName(studentEnrollmentAvaliation.StudentId));

                MailMessage mailMessage = new MailMessage();
                mailMessage.From = from;
                mailMessage.To.Add(to);

                mailMessage.Subject = GradesMailSubject();

                mailMessage.Body = GradesMailBody(GetTeacherName(),
                                                  studentEnrollmentAvaliation.Grade.ToString(),
                                                  GetActivityName(studentEnrollmentAvaliation.ActivityId),
                                                  GetDisciplineCode(studentEnrollmentAvaliation.DisciplineId),
                                                  GetDisciplineName(studentEnrollmentAvaliation.DisciplineId));

                SendMail(mailMessage);
            }
            catch (Exception ex)
            {
                Log.Instance.Error(ex);
            }
        }

        /// <summary>
        /// Método que envia ao aluno, com cópia para o setor pedagógico um e-mail de alerta de faltas.
        /// </summary>
        /// <param name="studentEnrollment">Objeto Matrícula</param>
        public void SendFaultsMail(StudentEnrollment studentEnrollment)
        {
            try
            {
                MailAddress from = new MailAddress(GetTeacherEmail(), GetTeacherName());
                MailAddress to = new MailAddress(GetStudentEmail(studentEnrollment.StudentId), GetStudentName(studentEnrollment.StudentId));
                MailAddress cc = new MailAddress(GetEducationalSectorEmail(), GetEducationalSectorName());

                MailMessage mailMessage = new MailMessage();
                mailMessage.From = from;
                mailMessage.To.Add(to);
                mailMessage.CC.Add(cc);
                
                mailMessage.Subject = FaultsMailSubject();
                
                mailMessage.Body = FaultsMailBody(GetTeacherName(), 
                                                  studentEnrollment.Faults.ToString(),
                                                  GetDisciplineMaxFaults(studentEnrollment.DisciplineId).ToString(), 
                                                  GetDisciplineCode(studentEnrollment.DisciplineId), 
                                                  GetDisciplineName(studentEnrollment.DisciplineId));

                SendMail(mailMessage);
            }
            catch (Exception ex)
            {
                Log.Instance.Error(ex);
            }
        }

        #endregion

        #region -Private Methods-

        #region -Get Data-

        /// <summary>
        /// Busca o nome do professor registrado no sistema.
        /// </summary>
        /// <returns>Nome do professor registrado no sistema</returns>
        private string GetTeacherName()
        {
            return AppConfig.ReadConfiguration(AppConfig.AppUser);
        }

        /// <summary>
        /// Busca o e-mail do professor registrado no sistema.
        /// </summary>
        /// <returns>E-mail do professor registrado no sistema</returns>
        private string GetTeacherEmail()
        {
            return AppConfig.ReadConfiguration(AppConfig.AppUserEmail);
        }

        /// <summary>
        /// Busca o nome do setor pedagógico registrado no sistema.
        /// </summary>
        /// <returns>Nome do setor pedagógico registrado no sistema</returns>
        private string GetEducationalSectorName()
        {
            return AppConfig.ReadConfiguration(AppConfig.EducationalSector);
        }

        /// <summary>
        /// Busca o e-mail do setor pedagógico registrado no sistema.
        /// </summary>
        /// <returns>E-mail do setor pedagógico registrado no sistema</returns>
        private string GetEducationalSectorEmail()
        {
            return AppConfig.ReadConfiguration(AppConfig.EducationalSectorEmail);
        }

        /// <summary>
        /// Busca o nome aluno.
        /// </summary>
        /// <param name="studentId">Id do aluno</param>
        /// <returns>Nome do aluno</returns>
        private string GetStudentName(int studentId)
        {
            try
            {
                Student student = new Student();
                student.Id = studentId;

                StudentBLL studentBLL = new StudentBLL();
                return new CultureInfo("en-US").TextInfo.ToTitleCase(studentBLL.GetStudentByParam(student).Rows[0]["NOME"].ToString().ToLower());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Busca o e-mail do aluno.
        /// </summary>
        /// <param name="studentId">Id do aluno</param>
        /// <returns>E-mail do aluno</returns>
        private string GetStudentEmail(int studentId)
        {
            try
            {
                Student student = new Student();
                student.Id = studentId;

                StudentBLL studentBLL = new StudentBLL();
                return studentBLL.GetStudentByParam(student).Rows[0]["EMAIL"].ToString().ToLower();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Busca o código da disciplina.
        /// </summary>
        /// <param name="disciplineId">Id da disciplina</param>
        /// <returns>Código da disciplina</returns>
        private string GetDisciplineCode(int disciplineId)
        {
            try
            {
                Discipline discipline = new Discipline();
                discipline.Id = disciplineId;

                DisciplineBLL disciplineBLL = new DisciplineBLL();
                return disciplineBLL.GetDisciplineByParam(discipline).Rows[0]["CODIGO"].ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Busca o nome da disciplina.
        /// </summary>
        /// <param name="disciplineId">Id da disciplina</param>
        /// <returns>Nome da disciplina</returns>
        private string GetDisciplineName(int disciplineId)
        {
            try
            {
                Discipline discipline = new Discipline();
                discipline.Id = disciplineId;

                DisciplineBLL disciplineBLL = new DisciplineBLL();
                return new CultureInfo("en-US").TextInfo.ToTitleCase(disciplineBLL.GetDisciplineByParam(discipline).Rows[0]["NOME"].ToString().ToLower());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Busca o número máximo de faltas permitidas na disciplina.
        /// </summary>
        /// <param name="disciplineId">Id da disciplina</param>
        /// <returns>Número máximo de faltas permitidas na disciplina</returns>
        private int GetDisciplineMaxFaults(int disciplineId)
        {
            try
            {
                Discipline discipline = new Discipline();
                discipline.Id = disciplineId;

                DisciplineBLL disciplineBLL = new DisciplineBLL();
                return int.Parse(disciplineBLL.GetDisciplineByParam(discipline).Rows[0]["MAXIMO_FALTAS_PERMITIDAS"].ToString());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Busca o nome da atividade.
        /// </summary>
        /// <param name="activityId">Id da atividade</param>
        /// <returns>Nome da atividade</returns>
        private string GetActivityName(int activityId)
        {
            try
            {
                Activity activity = new Activity();
                activity.Id = activityId;

                ActivityBLL activityBLL = new ActivityBLL();
                return new CultureInfo("en-US").TextInfo.ToTitleCase(activityBLL.GetActivityByParam(activity).Rows[0]["NOME"].ToString().ToLower());
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        #region -Grades-

        /// <summary>
        /// Método que define o assunto da mensagem de e-mail de aviso de notas.
        /// </summary>
        /// <returns>Assunto da mensagem de e-mail de aviso de notas</returns>
        private string GradesMailSubject()
        {
            return "SisCRA - Aviso de Notas";
        }

        /// <summary>
        /// Método que define o corpo da mensagem de e-mail de aviso de notas.
        /// </summary>
        /// <param name="teacherName">Nome do professor</param>
        /// <param name="grade">Nota</param>
        /// <param name="activityName">Nome da atividade</param>
        /// <param name="disciplineCode">Código da disciplina</param>
        /// <param name="disciplineName">Nome da disciplina</param>
        /// <returns>Corpo da mensagem de e-mail de aviso de notas</returns>
        private string GradesMailBody(string teacherName, string grade, string activityName, string disciplineCode, string disciplineName)
        {
            return string.Format("Caro Aluno(a),\r\n\r\nO Sistema de Controle de Redimento Acadêmico, em nome" +
            " do professor {0}, informa que você tirou {1} na atividade {2}, da disciplina {3} - {4}." +
            "\r\n\r\nAtenciosamente,\r\n\r\nSisCRA - Sistema de Controle de Rendimento Acadêmico.", teacherName, grade, activityName, disciplineCode, disciplineName);
        }

        #endregion

        #region -Faults-

        /// <summary>
        /// Método que define o assunto da mensagem de e-mail de alerta de faltas.
        /// </summary>
        /// <returns>Assunto da mensagem de e-mail de alerta de faltas</returns>
        private string FaultsMailSubject()
        {
            return "SisCRA - Alerta de Faltas";
        }

        /// <summary>
        /// Método que define o corpo da mensagem de e-mail de alerta de faltas.
        /// </summary>
        /// <param name="teacherName">Nome do professor</param>
        /// <param name="faults">Número de faltas</param>
        /// <param name="maxFaults">Número máximo de faltas</param>
        /// <param name="disciplineCode">Código da disciplina</param>
        /// <param name="disciplineName">Nome da disciplina</param>
        /// <returns>Corpo da mensagem de e-mail de alerta de faltas</returns>
        private string FaultsMailBody(string teacherName, string faults, string maxFaults, string disciplineCode, string disciplineName)
        {
            return string.Format("Caro Aluno(a),\r\n\r\nO Sistema de Controle de Redimento Acadêmico, em nome" +
            " do professor {0}, alerta que você está com {1} falta(s) de um total de {2} faltas permitidas" + 
            " na disciplina {3} - {4}. Fique atento(a) ao número de faltas, pois isso pode implicar em sua reprovação." +
            "\r\n\r\nAtenciosamente,\r\n\r\nSisCRA - Sistema de Controle de Rendimento Acadêmico.", teacherName, faults, maxFaults, disciplineCode, disciplineName);
        }

        #endregion

        /// <summary>
        /// Envia uma mensagem de e-mail utilizando as credenciais do usuário registrado no sistema.
        /// </summary>
        /// <param name="mailMessage">Mensagem a ser enviada</param>
        private void SendMail(MailMessage mailMessage)
        {
            try
            {
                using (this.smtpClient = new SmtpClient(SMTP_HOST, SMTP_PORT))
                {
                    smtpClient.UseDefaultCredentials = false;
                    smtpClient.Credentials = new NetworkCredential(AppConfig.ReadConfiguration(AppConfig.AppUserEmail), AppConfig.ReadConfiguration(AppConfig.AppUserPassword));
                    smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                    smtpClient.EnableSsl = true;
                    smtpClient.Timeout = 60000;

                    smtpClient.Send(mailMessage);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion
    }
}
